// Copyright (C) 2025, ZWSOFT Co., LTD.(Guangzhou)
// Licensed under the MIT License. See LICENSE in the project root for license information.

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
using System.Windows.Media;
using TestClient.Domain;

namespace TestClient.ViewModel;

public partial class CurveExtendInputRow : ObservableObject
{
    public string CurveId { get; set; } = "";
    public Vector3d Endpoint { get; set; }
    public double Offset { get; set; }
}

public partial class CurveAttributeInputRow : ObservableObject
{
    public string CurveId { get; set; } = "";
    public int Red { get; set; }
    public int Green { get; set; }
    public int Blue { get; set; }
    public double Width { get; set; }
}

public partial class CurveOperationTestViewModel : PageViewModelBase
{
    #region Extend & offset
    [ObservableProperty]
    private string surfaceId = "";
    [ObservableProperty]
    private string curveId = "";
    [ObservableProperty]
    private Vector3d endPoint;
    [ObservableProperty]
    private double offset;

    [ObservableProperty]
    private string startOffsetList = "";
    [ObservableProperty]
    private string endOffsetList = "";
    [ObservableProperty]
    private bool isBothSide;
    [ObservableProperty]
    private bool useRuled;
    [ObservableProperty]
    private string curveNames = "";
    [ObservableProperty]
    private string geosetName = "";
    [ObservableProperty]
    private Vector3d sideVec = new();

    [ObservableProperty]
    private string resultCurves = "";

    [ObservableProperty]
    private CurveExtendInputRow? selectedItem;

    public ObservableCollection<CurveExtendInputRow> CurveExtendInputRows { get; } = [];
    #endregion

    #region Curve Attribute
    [ObservableProperty]
    private string attrCurveId = "";
    [ObservableProperty]
    private Color color = Color.FromRgb(0, 250, 0);
    [ObservableProperty]
    private double width = 0.25;

    public ObservableCollection<double> WidthChoices { get; } = [0.18, 0.25, 0.35, 0.5, 0.7, 1];

    [ObservableProperty]
    private CurveAttributeInputRow? selectedAttributeItem;

    public ObservableCollection<CurveAttributeInputRow> CurveAttributeInputRows { get; } = [];
    #endregion

    #region Curve Coincidence
    [ObservableProperty]
    private string coinCurve1Id = "";
    [ObservableProperty]
    private string coinCurve2Id = "";
    [ObservableProperty]
    private int coincidenceResult = 0;
    #endregion

    #region Curve Join
    [ObservableProperty]
    private string curvesToJoinId = "";
    [ObservableProperty]
    private string joinedCurveId = "";
    #endregion

    #region Commands
    public IAsyncRelayCommand SelectSurfaceCommand { get; }
    public IAsyncRelayCommand SelectCurveCommand { get; }
    public IAsyncRelayCommand SelectEndPointCommand { get; }
    public IAsyncRelayCommand SelectDirVecCommand { get; }
    public IAsyncRelayCommand ExtendCurveCommand { get; }
    public IAsyncRelayCommand OffsetCurveCommand { get; }
    public IAsyncRelayCommand OffsetCurve2Command { get; }
    public IAsyncRelayCommand SetCurveAttrCommand { get; }
    public IAsyncRelayCommand GetCurveAttrCommand { get; }
    public IAsyncRelayCommand SelectCoinCurve1Command { get; }
    public IAsyncRelayCommand SelectCoinCurve2Command { get; }
    public IAsyncRelayCommand CheckCurveCoincidenceCommand { get; }
    public IAsyncRelayCommand SelectCurvesToJoinCommand { get; }
    public IAsyncRelayCommand JoinCurvesCommand { get; }
    #endregion

    public CurveOperationTestViewModel(VFAdapterWrapper wrapper, ISnackNotifyService notifyService) : base(wrapper, notifyService)
    {
        SelectSurfaceCommand = CreateAsyncRpcCommand(SelectSurface);
        SelectCurveCommand = CreateAsyncRpcCommand(SelectCurve);
        SelectEndPointCommand = CreateAsyncRpcCommand(SelectEndPoint);
        SelectDirVecCommand = CreateAsyncRpcCommand(SelectDirVec);
        ExtendCurveCommand = CreateAsyncRpcCommand(ExtendCurve);
        OffsetCurveCommand = CreateAsyncRpcCommand(OffsetCurve);
        OffsetCurve2Command = CreateAsyncRpcCommand(OffsetCurve2);
        SetCurveAttrCommand = CreateAsyncRpcCommand(SetCurveAttrAsync);
        GetCurveAttrCommand = CreateAsyncRpcCommand(GetCurveAttrAsync);
        SelectCoinCurve1Command = CreateAsyncRpcCommand(SelectCoinCurve1Async);
        SelectCoinCurve2Command = CreateAsyncRpcCommand(SelectCoinCurve2Async);
        CheckCurveCoincidenceCommand = CreateAsyncRpcCommand(CheckCurveCoincidenceAsync);
        SelectCurvesToJoinCommand = CreateAsyncRpcCommand(SelectCurvesToJoinAsync);
        JoinCurvesCommand = CreateAsyncRpcCommand(JoinCurvesAsync);
    }

    #region Curve Extend table actions
    [RelayCommand]
    private void AddCurveExtendInputRow()
    {
        CurveExtendInputRows.Add(new CurveExtendInputRow
        {
            CurveId = CurveId,
            Endpoint = EndPoint,
            Offset = Offset
        });
        CurveId = "";
        EndPoint = Vector3d.Zero;
        Offset = 0;
    }
    [RelayCommand]
    private void ClearCurveExtendInputRows()
    {
        CurveExtendInputRows.Clear();
    }
    [RelayCommand]
    private void DeleteItem()
    {
        if (SelectedItem != null)
        {
            CurveExtendInputRows.Remove(SelectedItem);
        }
    }
    #endregion

    #region Curve Attr table actions
    [RelayCommand]
    private void AddAttributeInputRow()
    {
        var attr = new CurveAttributeInputRow
        {
            CurveId = CurveId,
            Red = Color.R,
            Green = Color.G,
            Blue = Color.B,
            Width = Width
        };
        CurveAttributeInputRows.Add(attr);
    }
    [RelayCommand]
    private void ClearAttributeInputRows()
    {
        CurveAttributeInputRows.Clear();
    }
    [RelayCommand]
    private void DeleteAttrItem()
    {
        if (SelectedAttributeItem != null)
        {
            CurveAttributeInputRows.Remove(SelectedAttributeItem);
        }
    }
    #endregion

    #region Curve Extend Commands
    private async Task SelectSurface()
    {
        EnsureConnected();
        SurfaceId = await ViewModelHelpers.SelectEnt(Wrapper, "Surface");
    }
    private async Task SelectCurve()
    {
        EnsureConnected();
        CurveId = await ViewModelHelpers.SelectEnt(Wrapper, "Curve");
    }
    private async Task SelectEndPoint()
    {
        EnsureConnected();
        var endpointId = await ViewModelHelpers.SelectEnt(Wrapper, "Point");
        EndPoint = await Task.Run(() =>
        {
            return Wrapper.GetPointCoord(endpointId);
        });
    }
    private async Task SelectDirVec()
    {
        EnsureConnected();

        SideVec = await Task.Run(() =>
        {
            // pick point 1, pick point 2, get coord, sub to get a vector, and normalize it
            var point1Id = Wrapper.SelectionToId("Point");
            var point2Id = Wrapper.SelectionToId("Point");
            var coord1 = Wrapper.GetPointCoord(point1Id);
            var coord2 = Wrapper.GetPointCoord(point2Id);
            return (coord2 - coord1).Normalized();
        });
    }
    private async Task ExtendCurve()
    {
        EnsureConnected();
        var curves = CurveExtendInputRows.Select(r => r.CurveId).ToArray();
        var endpoints = CurveExtendInputRows.Select(r => r.Endpoint).ToArray();
        var offsets = CurveExtendInputRows.Select(r => r.Offset).ToArray();
        await Task.Run(() =>
        {
            Wrapper.TrimOrExtendCurvesFromEndPoint(curves, SurfaceId, endpoints, offsets);
        });
    }
    private async Task OffsetCurve()
    {
        EnsureConnected();
        string[] curveId;
        if (UseRuled)
        {
            // Law
            var startOffset = StartOffsetList.Split(',').Select(s => double.Parse(s)).ToArray();
            var endOffset = EndOffsetList.Split(',').Select(s => double.Parse(s)).ToArray();
            if (IsBothSide)
            {
                curveId = await Task.Run(() =>
                {
                    return Wrapper.OffsetCurvesByStartPointAndEndPointBySymmetry(SurfaceId, CurveId, startOffset, endOffset);
                });
            }
            else
            {
                curveId = await Task.Run(() =>
                {
                    return Wrapper.OffsetCurvesByStartPointAndEndPoint(SurfaceId, CurveId, "true", startOffset, endOffset);
                });
            }
        }
        else
        {
            // no ruled
            var offset = StartOffsetList.Split(',').Select(s => double.Parse(s)).ToArray();
            var curveNameList = CurveNames.Split(",").Select(s => s.Trim()).ToArray();
            if (IsBothSide)
            {
                curveId = await Task.Run(() =>
                {
                    return Wrapper.OffsetCurvesBySymmetry(SurfaceId, CurveId, offset);
                });
            }
            else
            {
                curveId = await Task.Run(() =>
                {
                    return Wrapper.OffsetCurvesByParallel(SurfaceId, CurveId, SideVec, GeosetName, curveNameList, offset);
                });
            }
        }
        ResultCurves = string.Join(",", curveId);
    }

    private async Task OffsetCurve2()
    {
        EnsureConnected();
        var curveNameList = CurveNames.Split(",").Select(s => s.Trim()).ToArray();
        var offset = StartOffsetList.Split(',').Select(s => double.Parse(s)).ToArray();

        var curveId = await Task.Run(() =>
        {
            return Wrapper.OffsetCurvesByParallel2(SurfaceId, CurveId, SideVec, GeosetName, curveNameList, offset);
        });

        ResultCurves = string.Join(",", curveId);
    }
    #endregion

    #region Curve Attr Commands
    private async Task SetCurveAttrAsync()
    {
        EnsureConnected();
        await Task.Run(() =>
        {
            var curveIds = CurveAttributeInputRows.Select(x => x.CurveId).ToArray();
            var redList = CurveAttributeInputRows.Select(x => x.Red).ToArray();
            var greenList = CurveAttributeInputRows.Select(x => x.Green).ToArray();
            var blueList = CurveAttributeInputRows.Select(x => x.Blue).ToArray();
            var widthList = CurveAttributeInputRows.Select(x => x.Width).ToArray();
            Wrapper.ChangeLinesColorAndWidth(curveIds, redList, greenList, blueList, widthList);
        });
    }
    private async Task GetCurveAttrAsync()
    {
        EnsureConnected();
        await Task.Run(() =>
        {
            var curveIds = CurveAttributeInputRows.Select(x => x.CurveId).ToArray();
            Wrapper.GetLinesColorAndWidth(curveIds, out var redList, out var greenList, out var blueList, out var widthList);
            for (int i = 0; i < curveIds.Length; i++)
            {
                CurveAttributeInputRows[i].Red = redList[i];
                CurveAttributeInputRows[i].Green = greenList[i];
                CurveAttributeInputRows[i].Blue = blueList[i];
                CurveAttributeInputRows[i].Width = widthList[i];
            }
        });
    }
    private async Task SelectCoinCurve1Async()
    {
        EnsureConnected();
        CoinCurve1Id = await ViewModelHelpers.SelectEnt(Wrapper, "Curve");
    }
    private async Task SelectCoinCurve2Async()
    {
        EnsureConnected();
        CoinCurve2Id = await ViewModelHelpers.SelectEnt(Wrapper, "Curve");
    }
    private async Task CheckCurveCoincidenceAsync()
    {
        EnsureConnected();
        CoincidenceResult = await Task.Run(() =>
        {
            return Wrapper.TestCurveCoincidence(CoinCurve1Id, CoinCurve2Id);
        });
    }
    #endregion

    #region Curve Join Commands
    private async Task SelectCurvesToJoinAsync()
    {
        EnsureConnected();
        var ids = await Task.Run(() =>
        {
            return Wrapper.MultiSelect("Curve");
        });
        CurvesToJoinId = string.Join(",", ids);
    }
    private async Task JoinCurvesAsync()
    {
        EnsureConnected();
        var ids = CurvesToJoinId.Split(",").Select(x => x.Trim()).ToArray();
        JoinedCurveId = await Task.Run(() =>
        {
            return Wrapper.CreateJoin(ids);
        });
    }
    #endregion
}
